#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# fixed2free2.py: Conversion of Fortran code from fixed to free
#                 source form.
#
# Copyright (C) 2012    Elias Rabel
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
Script that converts fixed form Fortran code to free form
Usage: file name as first command line parameter

python fixed2free2.py file.f > file.f90
"""

# author: Elias Rabel, 2012
# Let me know if you find this script useful:
# ylikx.0 at gmail
# https://www.github.com/ylikx/

# TODO:
# *) Improve command line usage

from __future__ import print_function
import sys

class FortranLine:
    def __init__(self, line, length_limit=True):
        self.line = line
        self.line_conv = line
        self.isComment = False
        self.isContinuation = False
        self.length_limit = length_limit
        self.__analyse()
        
    def __repr__(self):
        return self.line_conv
        
    def continueLine(self):
        """Insert line continuation symbol at end of line."""

        if not (self.isLong and self.is_regular):
            self.line_conv = self.line_conv.rstrip() + " &\n"
        else:
            temp = self.line_conv[:72].rstrip() + " &"
            self.line_conv = temp.ljust(72) + self.excess_line
                  
    def __analyse(self):
        line = self.line
        firstchar = line[0] if len(line) > 0 else ''
        self.label = line[0:5].strip().lower() + ' ' if len(line) > 1 else ''
        cont_char = line[5] if len(line) >= 6 else ''
        fivechars = line[1:5] if len(line) > 1 else ''
        self.isShort = (len(line) <= 6)
        self.isLong  = (len(line) > 73 and self.length_limit)
        
        self.isComment = firstchar in "cC*!"
        self.isNewComment = '!' in fivechars and not self.isComment
        self.isOMP = self.isComment and fivechars.lower() == "$omp"
        if self.isOMP:
            self.isComment = False
            self.label = ''
        self.isCppLine = (firstchar == '#')
        self.is_regular = (not (self.isComment or self.isNewComment or 
                           self.isCppLine or self.isShort))      
        self.isContinuation = (not (cont_char.isspace() or cont_char == '0') and
                               self.is_regular)

        if self.isLong and self.is_regular:
            self.excess_line = '!' + line[72:]
            line = line[:72] + '\n'
        else:
            self.excess_line = ''

        self.line = line
        self.__convert()

    def __convert(self):
        line = self.line
        self.code = line[6:] if len(line) > 6 else '\n'
        
        if self.isComment:
            self.line_conv = '!' + line[1:]
        elif self.isNewComment or self.isCppLine:
            self.line_conv = line
        elif self.isOMP:
            self.line_conv = '!' + line[1:5] + ' ' + self.code
        elif not self.label.isspace():
            self.line_conv = self.label + self.code
        else:
            self.line_conv = self.code

        if self.isLong and self.is_regular:
            self.line_conv = self.line_conv.rstrip().ljust(72) + self.excess_line


def convertToFree(stream, length_limit=True):
    """Convert stream from fixed source form to free source form."""
    linestack = []
        
    for line in stream:
        convline = FortranLine(line, length_limit)
        
        if convline.is_regular:
            if convline.isContinuation and linestack: 
                linestack[0].continueLine()
            for l in linestack:
                yield str(l)
            linestack = []
            
        linestack.append(convline)
        
    for l in linestack:
        yield str(l)
        

if __name__ == "__main__":

    if len(sys.argv) > 1:
        infile = open(sys.argv[1], 'r')
        for line in convertToFree(infile):
            print(line),
    
        infile.close()
    else:
        print(__doc__)
